--=========================================================================
-- copyright @2015 XavierCHN

-- @author XavierCHN
-- @date 2015.11.18
-- @changelog:

--[[
    README
        math.getrandom_withvar(baseval, randomval) -- 获取随机数，中心值为baseval，左右两侧范围为randomval
        math.getrandom(min, max) -- 获取随机数，最大值为min，最小值为max
        math.clamp(input, max_val) -- 使某个数字不能大于最大值或者最小值，如果大于或者小于，则从最小/最大值开始计算
        math.isnan(x) -- 是否无效值
        math.isinf(x) -- 是否无穷大
        math.isbad(x) -- 是否无效值或者无穷大
        math.round(num) -- 四舍五入
        string:split(sep) -- 拆分字符串
        string.trim(str) -- 去除字符串两端的空格
        string.ltrim(str) -- 去除字符串左侧的空格
        string.rtrim(str) -- 去除字符串右侧的空格
        string.removespaces(str) -- 去除字符串中的所有的空格
        string.cap(str) -- 将一个单词的第一个字母转换为大写
        string.count(str, substr, from, to) --统计str中substr出现的次数。from, to用于指定起始位置，缺省状态下from为1，to为字符串长度
        string.start(str, substr) --判断str是否以substr开头。是返回true，否返回false，失败返回失败信息
        string.startwith(str, substr) --判断str是否以substr开头。是返回true，否返回false，失败返回失败信息
        string.endwith(str, substr) --判断str是否以substr结尾。是返回true，否返回false，失败返回失败信息
        string.formatNumberThousands(num) -- 规范化数字的显示，将数字显示为 10,000,000
        table.contains(table, element) -- 判断表中是否包含某个元素
        RemoveByValue(t, value) -- 根据值移除某个元素
        GetTableSize(table) -- 获取表的大小
        GetRandomItem(choices) -- 从表中获取某个随机元素
        GetRandomItems(num, choices) -- 从表中获取几个随机元素，不允许重复
        GetRandomItemsWithDups(num, choices) -- 从表中获取几个随机元素，允许重复
        MergeTables(...) -- 合并几个表
        UnionTables(...) -- 合并几个表，重复的Value丢弃
        PushTables(...) -- 合并几个表，重复的Key覆盖
        shallowcopy(orig) -- 浅复制表
        deepcopy(object) -- 深复制表
        ExtendTable(origin, addition, multiple) -- 扩展表，给origin扩展multiple个addition
        ShuffleTable( orig_list ) -- 随机洗牌表
        PrintTable(tab) -- 打印表
        GetRandomKey(choices) -- 获取某个随机Key
        table.keys(t) -- 返回表中的所有key
        table.values(t) -- 返回表中的所有value
        table.containKey( t, key ) -- 判断表中是否包含key
        table.containValue( t, value ) -- 判断表中是否包含Value
        table.getKeyByValue( t, value ) -- 根据value获取Key
        table.merge(dest, src) -- 简单合并两个表
        RunInSandbox(untrusted_code) -- 在安全沙箱中运行代码
        distsq(v1, v2) -- 获取两个坐标的平方距离
]]

--=========================================================================


--*************************************************************************
-- #region 数字相关
--*************************************************************************

-- 生成随机数种子，避免随机结果不正确
local function __util_lua_setRandomSeed()
    local time = 0
    if os and os.time then
        time = os.time()
    else
        time = Time()
    end
    math.randomseed(tostring(time):reverse():sub(1, 6))  
end
__util_lua_setRandomSeed()

-- 获取随机数
function math.getrandom_withvar(baseval, randomval)
    return baseval + (math.random()*2*randomval - randomval)
end

-- 获取随机数
function math.getrandom(min, max)
    return min + math.random()*(max - min)
end

-- 使某个数字不能大于最大值或者最小值，如果大于或者小于，则从最小/最大值开始计算
function math.clamp(input, max_val)
    if input > max_val then
        return math.clamp(input - max_val, max_val)
    end
    return input
end

-- 无穷大
math.inf = 1/0

-- 是否无效值
function math.isnan(x)
    return x ~= x
end

-- 是否无穷大
function math.isinf(x)
    return x == math.inf or x == - math.inf
end

-- 是否无效值或者无穷大
function math.isbad(x)
    return math.isnan(x) or math.isinf(x)
end

-- 四舍五入
function math.round(num)
    return math.floor(num + 0.5)
end

--*************************************************************************
-- #endregion 数字相关
--*************************************************************************


--*************************************************************************
-- #region 字符串相关
--*************************************************************************

-- 拆分字符串
function string:split(sep)
    local sep, fields = sep or ":", {}
    local pattern = string.format("([^%s]+)", sep)
    self:gsub(pattern, function(c) fields[#fields+1] = c end)
    return fields
end

-- 去除字符串两端的空格
function string.trim(str)
    str = string.gsub(str, "^[ \t\n\r]+", "")
    return string.gsub(str, "[ \t\n\r]+$", "")
end

-- 去除字符串左侧的空格
function string.ltrim(str)
    return string.gsub(str, "^[ \t\n\r]+", "")
end

-- 去除字符串右侧的空格
function string.rtrim(str)
    return string.gsub(str, "[ \t\n\r]+$", "")
end

-- 去除字符串中的所有的空格
function string.removespaces(str)
    if str == nil then
        return nil, "the string parameter is nil"
    end
    str = string.gsub(str, " ", "")
    return str
end

-- 将一个单词的第一个字母转换为大写
function string.cap(str)
    if str == nil then
        return nil, "the string parameter is nil"
    end
    local ch = string.sub(str, 1, 1)
    local len = string.len(str)
    if ch < 'a' or ch > 'z' then
        return str
    end
    return string.upper(ch) .. string.sub(str, 2)
end

--统计str中substr出现的次数。from, to用于指定起始位置，缺省状态下from为1，to为字符串长度
function string.count(str, substr, from, to)
    if str == nil or substr == nil then
        return nil, "the string or the sub-string parameter is nil"
    end
    from = from or 1
    if to == nil or to > string.len(str) then
        to = string.len(str)
    end
    local str_tmp = string.sub(str, from ,to)
    local n = 0
    _, n = string.gsub(str, substr, '')
    return n
end

--判断str是否以substr开头。是返回true，否返回false，失败返回失败信息
function string.start(str, substr)
    if str == nil or substr == nil then
        return nil, "the string or the sub-stirng parameter is nil"
    end
    if string.find(str, substr) ~= 1 then
        return false
    else
        return true
    end
end

--判断str是否以substr开头。是返回true，否返回false，失败返回失败信息
function string.startwith(str, substr)
    return string.start(str, substr)
end

--判断str是否以substr结尾。是返回true，否返回false，失败返回失败信息
function string.endwith(str, substr)
    if str == nil or substr == nil then
        return nil, "the string or the sub-string parameter is nil"
    end
    str_tmp = string.reverse(str)
    substr_tmp = string.reverse(substr)
    if string.find(str_tmp, substr_tmp) ~= 1 then
        return false
    else
        return true
    end
end

-- 规范化数字的显示，将数字显示为 10,000,000
function string.formatNumberThousands(num)
    local formatted = tostring(tonumber(num))
    local k
    while true do
        formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
        if k == 0 then break end
    end
    return formatted
end
--*************************************************************************
-- #endregion 字符串相关
--*************************************************************************



--*************************************************************************
-- #region 表相关
--*************************************************************************

-- 判断表中是否包含某个元素
function table.contains(table, element)
    if table == nil then
        return false
    end
    
    for _, value in pairs(table) do
        if value == element then
            return true
        end
    end
    return false
end

-- 根据值移除某个元素
function RemoveByValue(t, value)
    if t then
        for i,v in ipairs(t) do 
            if v == value then
                table.remove(t, i)
            end
        end
    end
    return t
end

-- 获取表的大小
function GetTableSize(table)
    local n = 0
    if table ~= nil then
        for _ in pairs(table) do
            n = n + 1
        end
    end
    return n
end

-- 从表中获取某个随机元素
function GetRandomItem(choices)
    local choice = math.random(GetTableSize(choices)) -1
    
    local picked = nil
    for k,v in pairs(choices) do
        picked = v
        if choice<= 0 then
            break
        end
        choice = choice -1
    end
    assert(picked~=nil)
    return picked
end

-- 从表中获取几个随机元素，不允许重复
function GetRandomItems(num, choices)
    local l_choices = choices
    local ret = {}
    for i=1,num do
        local choice = GetRandomItem(choices)
        table.insert(ret, choice)
        table.remove(l_choices, choice)
    end
    return ret
end

-- 从表中获取几个随机元素，允许重复
function GetRandomItemsWithDups(num, choices)
    local l_choices = choices
    local ret = {}
    for i=1,num do
        local choice = math.random(#l_choices)
        print("insert ",l_choices[choice])
        table.insert(ret, l_choices[choice])       
    end
    return ret
end

-- 合并几个表
function MergeTables(...)
    local ret = {}
    for i,array in ipairs(arg) do
        for j,val in pairs(array) do
            table.insert(ret, val)
        end
    end
    return ret
end

-- 合并几个表，重复的Value丢弃
function UnionTables(...)
    local ret = {}
    for i,array in ipairs(arg) do
        for j,val in pairs(array) do
            if not table.contains(ret, val) then
                table.insert(ret, val)
            end
        end
    end
    return ret
end

-- 合并几个表，重复的Key覆盖
function PushTables(...)
    local ret = {}
    for i,map in ipairs(arg) do
        for k,v in pairs(map) do
            ret[k] = v
        end
    end
    return ret
end

-- 浅复制表
function shallowcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in pairs(orig) do
            copy[orig_key] = orig_value
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

-- 深复制表
function deepcopy(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for index, value in pairs(object) do
            new_table[_copy(index)] = _copy(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

-- 扩展表，给origin扩展multiple个addition
function ExtendTable(origin, addition, multiple)
    local ret = {}
    for k,v in pairs(origin) do
        ret[k] = v
    end
    multiple = multiple or 1
    for i=1,multiple do
        table.insert(ret,addition)
    end
    return ret
end

-- 随机洗牌表
function ShuffleTable( orig_list )
    local list = shallowcopy( orig_list )
    local result = {}
    local count = #list
    for i = 1, count do
        local pick = RandomInt( 1, #list )
        result[ #result + 1 ] = list[ pick ]
        table.remove( list, pick )
    end
    return result
end

-- 打印表
function PrintTable(tab)
    local str = {}
    
    local function internal(tab, str, indent)
        for k,v in pairs(tab) do
            if type(v) == "table" then
                table.insert(str, indent..tostring(k)..":\n")
                internal(v, str, indent..' ')
            else
                table.insert(str, indent..tostring(k)..": "..tostring(v).."\n")
            end
        end
    end
    
    internal(tab, str, '')
    print(table.concat(str, ''))
end

-- 获取某个随机Key
function GetRandomKey(choices)
    local choice = math.random(GetTableSize(choices)) -1
    
    local picked = nil
    for k,v in pairs(choices) do
        picked = k
        if choice<= 0 then
            break
        end
        choice = choice -1
    end
    assert(picked)
    return picked
end

-- 返回表中的所有key
function table.keys(t)
    local keys = {}
    if t == nil then
        return keys
    end
    for k, v in pairs(t) do
        keys[#keys + 1] = k
    end
    return keys
end

-- 返回表中的所有value
function table.values(t)
    local values = {}
    if t == nil then
        return values
    end
    for k, v in pairs(t) do
        values[#values + 1] = v
    end
    return values
end

-- 判断表中是否包含key
function table.containKey( t, key )
    for k, v in pairs(t) do
        if key == k then
            return true
        end
    end
    return false
end

-- 判断表中是否包含Value
function table.containValue( t, value )
    for k, v in pairs(t) do
        if value == v then
            return true
        end
    end
    return false
end

-- 根据value获取Key
function table.getKeyByValue( t, value )
    for k, v in pairs(t) do
        if value == v then
            return k
        end
    end
end

-- 简单合并两个表
function table.merge(dest, src)
    for k, v in pairs(src) do
        dest[k] = v
    end
end

--*************************************************************************
-- #endregion 表相关
--*************************************************************************


--*************************************************************************
-- #region 杂项
--*************************************************************************

-- 运行环境
local env = {}

-- 在安全沙箱中运行代码
function RunInSandbox(untrusted_code)
    if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end
    local untrusted_function, message = loadstring(untrusted_code)
    if not untrusted_function then return nil, message end
    setfenv(untrusted_function, env)
    return pcall(untrusted_function)
end

-- 获取两个坐标的平方距离
function distsq(v1, v2)
    local dx = (v1.x or v1[1]) - (v2.x or v2[1])
    local dy = (v1.y or v1[2]) - (v2.y or v2[2])
    local dz = (v1.z or v1[3]) - (v2.z or v2[3])
    return dx*dx+dy*dy+dz*dz
end

--*************************************************************************
-- #endregion 杂项
--*************************************************************************